﻿' Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

Imports System.Globalization
Imports System.Text
Imports System.Xml.Linq
Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests.Symbols
Imports Roslyn.Test.Utilities
Imports System.Collections.Immutable

Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests

    Public Class ContainerTests
        Inherits BasicTestBase

        ' Check that "basRootNS" is actually a bad root namespace
        Private Sub BadDefaultNS(badRootNS As String)
            AssertTheseDiagnostics(New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithRootNamespace(badRootNS).Errors,
<expected>
BC2014: the value '<%= badRootNS %>' is invalid for option 'RootNamespace'
</expected>)
        End Sub

        <Fact>
        Public Sub SimpleAssembly()

            Dim compilation = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="Banana">
    <file name="b.vb">
        Namespace NS
            Public Class Foo
            End Class
        End Namespace
    </file>
</compilation>)

            Dim sym = compilation.Assembly
            Assert.Equal("Banana", sym.Name)
            Assert.Equal("Banana, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", sym.ToTestDisplayString())
            Assert.Equal(String.Empty, sym.GlobalNamespace.Name)
            Assert.Equal(SymbolKind.Assembly, sym.Kind)
            Assert.Equal(Accessibility.NotApplicable, sym.DeclaredAccessibility)
            Assert.False(sym.IsShared)
            Assert.False(sym.IsOverridable)
            Assert.False(sym.IsOverrides)
            Assert.False(sym.IsMustOverride)
            Assert.False(sym.IsNotOverridable)
            Assert.Null(sym.ContainingAssembly)
            Assert.Null(sym.ContainingSymbol)
        End Sub

        <WorkItem(537302, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537302")>
        <Fact>
        Public Sub SourceModule()

            Dim compilation = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="Banana">
    <file name="m.vb">
        Namespace NS
            Public Class Foo
            End Class
        End Namespace
    </file>
</compilation>)

            Dim sym = compilation.SourceModule
            Assert.Equal("Banana.dll", sym.Name)
            Assert.Equal(String.Empty, sym.GlobalNamespace.Name)
            Assert.Equal(SymbolKind.NetModule, sym.Kind)
            Assert.Equal(Accessibility.NotApplicable, sym.DeclaredAccessibility)
            Assert.False(sym.IsShared)
            Assert.False(sym.IsOverridable)
            Assert.False(sym.IsOverrides)
            Assert.False(sym.IsMustOverride)
            Assert.False(sym.IsNotOverridable)
            Assert.Equal("Banana", sym.ContainingAssembly.Name)
            Assert.Equal("Banana", sym.ContainingSymbol.Name)
        End Sub

        <WorkItem(537421, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537421")>
        <Fact>
        Public Sub StandardModule()

            Dim compilation = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="Banana">
    <file name="m.vb">
    Namespace NS
        Module MFoo
          Dim A As Integer
          Sub MySub()
          End Sub
          Function Func(x As Long) As Long
            Return x
          End Function
        End Module
    End Namespace
    </file>
</compilation>)

            Dim ns As NamespaceSymbol = DirectCast(compilation.SourceModule.GlobalNamespace.GetMembers("NS").Single(), NamespaceSymbol)
            Dim sym1 = ns.GetMembers("MFoo").Single()
            Assert.Equal("MFoo", sym1.Name)
            Assert.Equal("NS.MFoo", sym1.ToTestDisplayString())
            Assert.Equal(SymbolKind.NamedType, sym1.Kind)
            ' default - Friend
            Assert.Equal(Accessibility.Friend, sym1.DeclaredAccessibility)
            Assert.False(sym1.IsShared)
            Assert.False(sym1.IsOverridable)
            Assert.False(sym1.IsOverrides)
            Assert.False(sym1.IsMustOverride)
            Assert.False(sym1.IsNotOverridable)
            Assert.Equal("Banana", sym1.ContainingAssembly.Name)
            Assert.Equal("NS", sym1.ContainingSymbol.Name)

            ' module member
            Dim smod = DirectCast(sym1, NamedTypeSymbol)
            Dim sym2 = DirectCast(smod.GetMembers("A").Single(), FieldSymbol)
            ' default - Private
            Assert.Equal(Accessibility.Private, sym2.DeclaredAccessibility)
            Assert.True(sym2.IsShared)

            Dim sym3 = DirectCast(smod.GetMembers("MySub").Single(), MethodSymbol)
            Dim sym4 = DirectCast(smod.GetMembers("Func").Single(), MethodSymbol)
            ' default - Public
            Assert.Equal(Accessibility.Public, sym3.DeclaredAccessibility)
            Assert.Equal(Accessibility.Public, sym4.DeclaredAccessibility)
            Assert.True(sym3.IsShared)
            Assert.True(sym4.IsShared)

            ' shared cctor
            'sym4 = DirectCast(smod.GetMembers(WellKnownMemberNames.StaticConstructorName).Single(), MethodSymbol)

        End Sub

        ' Check that we disallow certain kids of bad root namespaces
        <Fact>
        Public Sub BadDefaultNSTest()
            BadDefaultNS("Foo.7")
            BadDefaultNS("Foo..Bar")
            BadDefaultNS(".X")
            BadDefaultNS("$")
        End Sub

        ' Check that parse errors are reported 
        <Fact>
        Public Sub NamespaceParseErrors()
            Dim compilation = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="Banana">
    <file name="a.vb">
        Imports System.7
    </file>
    <file name="b.vb">
        Namespace Foo
        End Class
    </file>
</compilation>)

            Dim expectedErrors = <errors>
BC30205: End of statement expected.
Imports System.7
              ~~
BC30626: 'Namespace' statement must end with a matching 'End Namespace'.
Namespace Foo
~~~~~~~~~~~~~
BC30460: 'End Class' must be preceded by a matching 'Class'.
        End Class
        ~~~~~~~~~
                                 </errors>

            CompilationUtils.AssertTheseParseDiagnostics(compilation, expectedErrors)
        End Sub

        ' Check namespace symbols
        <Fact>
        Public Sub NSSym()
            Dim compilation = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="C">
    <file name="a.vb">
Namespace A
End Namespace

Namespace C
End Namespace
    </file>

    <file name="b.vb">
Namespace A.B
End Namespace

Namespace a.B
End Namespace

Namespace e
End Namespace
    </file>

    <file name="c.vb">
Namespace A.b.D
End Namespace
    </file>
</compilation>)

            Dim globalNS = compilation.SourceModule.GlobalNamespace

            Assert.Equal("", globalNS.Name)
            Assert.Equal(SymbolKind.Namespace, globalNS.Kind)
            Assert.Equal(3, globalNS.GetMembers().Length())

            Dim members = globalNS.GetMembers().AsEnumerable().OrderBy(Function(s) s.Name, IdentifierComparison.Comparer).ToArray()
            Dim membersA = globalNS.GetMembers("a")
            Dim membersC = globalNS.GetMembers("c")
            Dim membersE = globalNS.GetMembers("E")
            Assert.Equal(3, members.Length)
            Assert.Equal(1, membersA.Length)
            Assert.Equal(1, membersC.Length)
            Assert.Equal(1, membersE.Length)
            Assert.True(members.SequenceEqual(membersA.Concat(membersC).Concat(membersE).AsEnumerable()))
            Assert.Equal("a", membersA.First().Name, IdentifierComparison.Comparer)
            Assert.Equal("C", membersC.First().Name, IdentifierComparison.Comparer)
            Assert.Equal("E", membersE.First().Name, IdentifierComparison.Comparer)

            Dim nsA As NamespaceSymbol = DirectCast(membersA.First(), NamespaceSymbol)
            Assert.Equal("A", nsA.Name, IdentifierComparison.Comparer)
            Assert.Equal(SymbolKind.Namespace, nsA.Kind)
            Assert.Equal(1, nsA.GetMembers().Length())

            Dim nsB As NamespaceSymbol = DirectCast(nsA.GetMembers().First(), NamespaceSymbol)
            Assert.Equal("B", nsB.Name, IdentifierComparison.Comparer)
            Assert.Equal(SymbolKind.Namespace, nsB.Kind)
            Assert.Equal(1, nsB.GetMembers().Length())

            Dim nsD As NamespaceSymbol = DirectCast(nsB.GetMembers().First(), NamespaceSymbol)
            Assert.Equal("D", nsD.Name)
            Assert.Equal(SymbolKind.Namespace, nsD.Kind)
            Assert.Equal(0, nsD.GetMembers().Length())

            AssertTheseDeclarationDiagnostics(compilation,
<expected>
BC40055: Casing of namespace name 'a' does not match casing of namespace name 'A' in 'a.vb'.
Namespace a.B
          ~
BC40055: Casing of namespace name 'b' does not match casing of namespace name 'B' in 'b.vb'.
Namespace A.b.D
            ~
</expected>)
        End Sub

        ' Check namespace symbols in the presence of a root namespace
        <Fact>
        Public Sub NSSymWithRootNamespace()
            Dim compilation = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="C">
    <file name="a.vb">
Namespace A
End Namespace
Namespace E
End Namespace
    </file>
    <file name="b.vb">
Namespace A.B
End Namespace
Namespace C
End Namespace
    </file>
</compilation>, options:=TestOptions.ReleaseExe.WithRootNamespace("Foo.Bar"))

            Dim globalNS = compilation.SourceModule.GlobalNamespace

            Assert.Equal("", globalNS.Name)
            Assert.Equal(SymbolKind.Namespace, globalNS.Kind)
            Assert.Equal(1, globalNS.GetMembers().Length())

            Dim members = globalNS.GetMembers()
            Dim nsFoo As NamespaceSymbol = DirectCast(members.First(), NamespaceSymbol)
            Assert.Equal("Foo", nsFoo.Name, IdentifierComparison.Comparer)
            Assert.Equal(SymbolKind.Namespace, nsFoo.Kind)
            Assert.Equal(1, nsFoo.GetMembers().Length())

            Dim membersFoo = nsFoo.GetMembers()
            Dim nsBar As NamespaceSymbol = DirectCast(membersFoo.First(), NamespaceSymbol)
            Assert.Equal("Bar", nsBar.Name, IdentifierComparison.Comparer)
            Assert.Equal(SymbolKind.Namespace, nsBar.Kind)
            Assert.Equal(3, nsBar.GetMembers().Length())

            Dim membersBar = nsBar.GetMembers().AsEnumerable().OrderBy(Function(s) s.Name, IdentifierComparison.Comparer).ToArray()
            Dim membersA = nsBar.GetMembers("a")
            Dim membersC = nsBar.GetMembers("c")
            Dim membersE = nsBar.GetMembers("E")
            Assert.Equal(3, membersBar.Length)
            Assert.Equal(1, membersA.Length)
            Assert.Equal(1, membersC.Length)
            Assert.Equal(1, membersE.Length)
            Assert.True(membersBar.SequenceEqual(membersA.Concat(membersC).Concat(membersE).AsEnumerable()))
            Assert.Equal("a", membersA.First().Name, IdentifierComparison.Comparer)
            Assert.Equal("C", membersC.First().Name, IdentifierComparison.Comparer)
            Assert.Equal("E", membersE.First().Name, IdentifierComparison.Comparer)

            Dim nsA As NamespaceSymbol = DirectCast(membersA.First(), NamespaceSymbol)
            Assert.Equal("A", nsA.Name, IdentifierComparison.Comparer)
            Assert.Equal(SymbolKind.Namespace, nsA.Kind)
            Assert.Equal(1, nsA.GetMembers().Length())

            Dim nsB As NamespaceSymbol = DirectCast(nsA.GetMembers().First(), NamespaceSymbol)
            Assert.Equal("B", nsB.Name, IdentifierComparison.Comparer)
            Assert.Equal(SymbolKind.Namespace, nsB.Kind)
            Assert.Equal(0, nsB.GetMembers().Length())

            CompilationUtils.AssertNoDeclarationDiagnostics(compilation)
        End Sub

        ' Check namespace symbol containers in the presence of a root namespace
        <Fact>
        Public Sub NSContainersWithRootNamespace()
            Dim compilation = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="C">
    <file name="a.vb">
Class Type1
End Class
    </file>
</compilation>, options:=TestOptions.ReleaseExe.WithRootNamespace("Foo.Bar"))

            Dim globalNS = compilation.SourceModule.GlobalNamespace
            Dim members = globalNS.GetMembers()
            Dim nsFoo As NamespaceSymbol = DirectCast(members.First(), NamespaceSymbol)
            Dim membersFoo = nsFoo.GetMembers()
            Dim nsBar As NamespaceSymbol = DirectCast(membersFoo.First(), NamespaceSymbol)
            Dim type1Sym = nsBar.GetMembers("Type1").Single()

            Assert.Same(nsBar, type1Sym.ContainingSymbol)
            Assert.Same(nsFoo, nsBar.ContainingSymbol)
            Assert.Same(globalNS, nsFoo.ContainingSymbol)
            Assert.Same(compilation.SourceModule, globalNS.ContainingSymbol)
        End Sub

        ' Check namespace symbol containers in the presence of a root namespace
        <Fact>
        Public Sub NSContainersWithoutRootNamespace()
            Dim compilation = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="C">
    <file name="a.vb">
Class Type1
End Class
    </file>
</compilation>)
            Dim globalNS = compilation.SourceModule.GlobalNamespace
            Dim type1Sym = globalNS.GetMembers("Type1").Single()

            Assert.Same(globalNS, type1Sym.ContainingSymbol)
            Assert.Same(compilation.SourceModule, globalNS.ContainingSymbol)
        End Sub

        <Fact>
        Public Sub ImportsAlias01()
            Dim compilation = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="Test">
    <file name="a.vb">
Imports ALS = N1.N2

Namespace N1
    Namespace N2
        Public Class A
           Sub S()
           End Sub
        End Class
    End Namespace
End Namespace

Namespace N3
    Public Class B
        Inherits ALS.A
    End Class
End Namespace
    </file>
    <file name="b.vb">
Imports ANO = N3
Namespace N1.N2
    Class C
        Inherits ANO.B
    End Class
End Namespace
    </file>
</compilation>)

            Dim globalNS = compilation.SourceModule.GlobalNamespace

            Dim n3 = DirectCast(globalNS.GetMembers("N3").Single(), NamespaceSymbol)
            Dim mem1 = DirectCast(n3.GetTypeMembers("B").Single(), NamedTypeSymbol)
            Assert.Equal("A", mem1.BaseType.Name)
            Assert.Equal("N1.N2.A", mem1.BaseType.ToTestDisplayString())

            Dim n1 = DirectCast(globalNS.GetMembers("N1").Single(), NamespaceSymbol)
            Dim n2 = DirectCast(n1.GetMembers("N2").Single(), NamespaceSymbol)
            Dim mem2 = DirectCast(n2.GetTypeMembers("C").Single(), NamedTypeSymbol)
            Assert.Equal("B", mem2.BaseType.Name)
            Assert.Equal("N3.B", mem2.BaseType.ToTestDisplayString())

            CompilationUtils.AssertNoDeclarationDiagnostics(compilation)
        End Sub

        <Fact, WorkItem(544009, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544009")>
        Public Sub MultiModulesNamespace()

            Dim text3 = <![CDATA[
    Namespace N1
      Structure SFoo
      End Structure
    End Namespace
    ]]>.Value

            Dim comp1 = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="Test1">
    <file name="a.vb">
    Namespace N1
      Class CFoo
      End Class
    End Namespace
    </file>
</compilation>)

            Dim comp2 = CompilationUtils.CreateCompilationWithMscorlib(
<compilation name="Test2">
    <file name="b.vb">
    Namespace N1
      Interface IFoo
      End Interface
    End Namespace
    </file>
</compilation>)

            Dim compRef1 = New VisualBasicCompilationReference(comp1)
            Dim compRef2 = New VisualBasicCompilationReference(comp2)

            Dim comp = VisualBasicCompilation.Create("Test3", {VisualBasicSyntaxTree.ParseText(text3)}, {MscorlibRef, compRef1, compRef2})

            Dim globalNS = comp.GlobalNamespace
            Dim ns = DirectCast(globalNS.GetMembers("N1").Single(), NamespaceSymbol)
            Assert.Equal(3, ns.GetTypeMembers().Length())
            Dim ext = ns.Extent
            Assert.Equal(NamespaceKind.Compilation, ext.Kind)
            Assert.Equal("Compilation: " & GetType(VisualBasicCompilation).FullName, ext.ToString())

            Dim constituents = ns.ConstituentNamespaces
            Assert.Equal(3, constituents.Length)
            Assert.True(constituents.Contains(TryCast(comp.SourceAssembly.GlobalNamespace.GetMembers("N1").Single(), NamespaceSymbol)))
            Assert.True(constituents.Contains(TryCast(comp.GetReferencedAssemblySymbol(compRef1).GlobalNamespace.GetMembers("N1").Single(), NamespaceSymbol)))
            Assert.True(constituents.Contains(TryCast(comp.GetReferencedAssemblySymbol(compRef2).GlobalNamespace.GetMembers("N1").Single(), NamespaceSymbol)))

            For Each constituentNs In constituents
                Assert.Equal(NamespaceKind.Module, constituentNs.Extent.Kind)
                Assert.Equal(ns.ToTestDisplayString(), constituentNs.ToTestDisplayString())
            Next
        End Sub

        <WorkItem(537310, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537310")>
        <Fact>
        Public Sub MultiModulesNamespaceCorLibraries()

            Dim text1 = <![CDATA[
    Namespace N1
      Class CFoo
      End Class
    End Namespace
    ]]>.Value

            Dim text2 = <![CDATA[
    Namespace N1
      Interface IFoo
      End Interface
    End Namespace
    ]]>.Value

            Dim text3 = <![CDATA[
    Namespace N1
      Structure SFoo
      End Structure
    End Namespace
    ]]>.Value

            Dim comp1 = VisualBasicCompilation.Create("Test1", syntaxTrees:={VisualBasicSyntaxTree.ParseText(text1)})
            Dim comp2 = VisualBasicCompilation.Create("Test2", syntaxTrees:={VisualBasicSyntaxTree.ParseText(text2)})

            Dim compRef1 = New VisualBasicCompilationReference(comp1)
            Dim compRef2 = New VisualBasicCompilationReference(comp2)

            Dim comp = VisualBasicCompilation.Create("Test3", {VisualBasicSyntaxTree.ParseText(text3)}, {compRef1, compRef2})

            Dim globalNS = comp.GlobalNamespace
            Dim ns = DirectCast(globalNS.GetMembers("N1").Single(), NamespaceSymbol)
            Assert.Equal(3, ns.GetTypeMembers().Length())
            Assert.Equal(NamespaceKind.Compilation, ns.Extent.Kind)

            Dim constituents = ns.ConstituentNamespaces
            Assert.Equal(3, constituents.Length)
            Assert.True(constituents.Contains(TryCast(comp.SourceAssembly.GlobalNamespace.GetMembers("N1").Single(), NamespaceSymbol)))
            Assert.True(constituents.Contains(TryCast(comp.GetReferencedAssemblySymbol(compRef1).GlobalNamespace.GetMembers("N1").Single(), NamespaceSymbol)))
            Assert.True(constituents.Contains(TryCast(comp.GetReferencedAssemblySymbol(compRef2).GlobalNamespace.GetMembers("N1").Single(), NamespaceSymbol)))

            For Each constituentNs In constituents
                Assert.Equal(NamespaceKind.Module, constituentNs.Extent.Kind)
                Assert.Equal(ns.ToTestDisplayString(), constituentNs.ToTestDisplayString())
            Next
        End Sub

        <WorkItem(690871, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/690871")>
        <Fact>
        Public Sub SpecialTypesAndAliases()
            Dim source =
                <compilation name="C">
                    <file>
Public Class C
End Class
                    </file>
                </compilation>

            Dim aliasedCorlib = TestReferences.NetFx.v4_0_30319.mscorlib.WithAliases(ImmutableArray.Create("Foo"))

            Dim comp = CreateCompilationWithReferences(source, {aliasedCorlib})

            ' NOTE: this doesn't compile in dev11 - it reports that it cannot find System.Object.
            ' However, we've already changed how special type lookup works, so this is not a major issue.
            comp.AssertNoDiagnostics()

            Dim objectType = comp.GetSpecialType(SpecialType.System_Object)
            Assert.Equal(TypeKind.Class, objectType.TypeKind)
            Assert.Equal("System.Object", objectType.ToTestDisplayString())

            Assert.Equal(objectType, comp.Assembly.GetSpecialType(SpecialType.System_Object))
            Assert.Equal(objectType, comp.Assembly.CorLibrary.GetSpecialType(SpecialType.System_Object))
        End Sub

        <WorkItem(690871, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/690871")>
        <Fact>
        Public Sub WellKnownTypesAndAliases()
            Dim [lib] =
                <compilation name="lib">
                    <file>
Namespace System.Threading.Tasks
    Public Class Task
        Public Status As Integer
    End Class
End Namespace
                    </file>
                </compilation>

            Dim source =
                <compilation name="test">
                    <file>
Imports System.Threading.Tasks

Public Class App
    Public T as Task
End Class
                    </file>
                </compilation>

            Dim libComp = CreateCompilationWithReferences([lib], {MscorlibRef_v4_0_30316_17626})
            Dim libRef = libComp.EmitToImageReference(aliases:=ImmutableArray.Create("myTask"))

            Dim comp = CreateCompilationWithReferences(source, {libRef, MscorlibRef_v4_0_30316_17626, MsvbRef_v4_0_30319_17929})

            ' NOTE: Unlike in C#, aliases on metadata references are ignored, so the
            ' reference to System.Threading.Tasks is ambiguous.
            comp.AssertTheseDiagnostics(
                <expected>
BC30560: 'Task' is ambiguous in the namespace 'System.Threading.Tasks'.
    Public T as Task
                ~~~~
                </expected>)
        End Sub

    End Class

End Namespace
